/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/common.hpp>
#include <baja/graphics.hpp>
#include <journal/app/constants.h>

namespace journal { namespace common { 

namespace g = baja::graphics;
namespace m = baja::math;

inline void validatePageCacheSize()
{
    // do a check on the cache size, make sure we are good
    // TRACK: in the future, instead of failing, we should handle this and just resize the cache
    // NOTE: IMPORTANT:
    // we need to make sure the page cache is at least as large as the number of pages we should at once in the screen
    // some math:
    // the largest view has 3 rows of data, each page with an aspect ratio of 6/8.
    // if we normalize the screen height (SH), the normalized screen width (NSW) becomes SW/SH.
    // likewise, normalizing a page's height (PH), NPW becomes PW/PH
    // now we have three rows, which gives us NPW = PW / (PH * 3)
    // so the number of pages that will fit in that width is 
    //   NSW / NPW = (SW/SH) / (PW / PH * 3), so
    //   NSW / NPW = (3 * PH * SW) / (SH * PW)
    // multiply that by the number of rows (3):
    //   number of pages = (ROWS * ROWS * PH * SW) / (SH * PW), or
    //   number of pages = SCREEN_ASPECT_RATIO * (ROWS * ROWS * PH) / PW)
    //
    // for example, in a resolution of 1366x768, aspect ratio is 1.778, the number of pages that will fit is 
    //   1.778 * 3 * 3 * 8 / 6 = 21
    // this is assuming no gap between the pages, by the way (but let's keep it that way for simplicity)
    // 
#ifdef BAJA_DEBUG
    b::float32 screenRatio = constants::shellService()->windowSize().x / constants::shellService()->windowSize().y;
    b::float32 t = constants::general::pageWorldDimensions().y / constants::general::pageWorldDimensions().x;
    t *= (constants::general::gridLayoutPageCountY() * constants::general::gridLayoutPageCountY()); 
    
    b::float32 maxPages = ceil(t * screenRatio);

    BAJA_ASSERT(maxPages <= constants::general::pageViewCacheCapacity(), "Page view cache is too small!");
#endif
}

inline const g::viewport getViewport(const m::point2d& dimensions)
{
    g::viewport viewport;
    viewport.position.x = 0.0f;
    viewport.position.y = 0.0f;
    viewport.width = dimensions.x;
    viewport.height = dimensions.y;
    viewport.minZ = 0.0f;
    viewport.maxZ = 1.0f;

    return viewport;
}

inline const g::viewport fullPageViewport()
{
    return getViewport(constants::general::pagePixelDimensions());
}

inline const m::rectangle fullPageBounds()
{
    return m::rectangle(
        -constants::general::pageWorldDimensions().x/2, 
        constants::general::pageWorldDimensions().y/2,
        constants::general::pageWorldDimensions().x/2, 
        -constants::general::pageWorldDimensions().y/2
        );
}

inline void spin(const double ms)
{
    b::stopwatch sw;
    while (sw.getElapsedMilliseconds() < ms);
}

inline view::layout_id commandIdToLayoutId(const view::command_id id)
{
    switch (id)
    {
    case view::command_id::zoom_fit_to_width: return view::layout_id::fitToWidth;
    case view::command_id::zoom_full_page: return view::layout_id::full;
    case view::command_id::zoom_grid: return view::layout_id::grid;
    }

    return view::layout_id::none;
}

inline int commandIdToInkTool(const view::command_id id)
{
    switch (id)
    {
        case view::command_id::tool_pen: return 1;
        case view::command_id::tool_eraser: return 2;
    }
    return 0;
}

inline view::command_id inkToolToCommandId(const int tool)
{
    switch (tool)
    {
        case 1: return view::command_id::tool_pen;
        case 2: return view::command_id::tool_eraser;
    }
    return view::command_id::none;
}

inline b::float32 commandIdToWidth(const view::command_id id)
{
    switch(id)
    {
        case view::command_id::pen_1: return 0.0075f;
        case view::command_id::pen_2: return 0.015f;
        case view::command_id::pen_3: return 0.04f;
        case view::command_id::pen_4: return 0.09f;
        case view::command_id::pen_5: return 0.14f;
    }

    return 0.f;
}

inline view::command_id widthToCommandId(const b::float32 width)
{
    if (width <= 0.015f) return view::command_id::pen_1;
    else if (width <= 0.04f) return view::command_id::pen_2;
    else if (width <= 0.09f) return view::command_id::pen_3;
    else if (width <= 0.14f) return view::command_id::pen_4;
    else if (width <= 0.2f) return view::command_id::pen_5;

    return view::command_id::none;
}

inline int commandIdToColor(const view::command_id id)
{
    switch(id)
    {
        case view::command_id::color_black: return 1;
        case view::command_id::color_blue: return 5;
        case view::command_id::color_yellow: return 3;
        case view::command_id::color_green: return 6;
        case view::command_id::color_pink: return 7;
        case view::command_id::color_red: return 2;
    }

    return -1;
}

inline view::command_id colorToCommandId(const int color)
{
    switch (color)
    {
        case 1: return view::command_id::color_black;
        case 2: return view::command_id::color_red;
        case 3: return view::command_id::color_yellow;
        case 5: return view::command_id::color_blue;
        case 6: return view::command_id::color_green;
        case 7: return view::command_id::color_pink;
    }

    return view::command_id::none;
}

inline view::command_id paperTypeToCommandId(const int paperType)
{
    switch (paperType)
    {
        case 0: return view::command_id::paper_0;
        case 1: return view::command_id::paper_1;
        case 2: return view::command_id::paper_2;
        case 3: return view::command_id::paper_3;
        case 4: return view::command_id::paper_4;
    }

    return view::command_id::none;
}

inline b::wchar* paperTypeToFileName(const view::paper_type paperType)
{
    switch (paperType)
    {
    case view::paper_type::paper_0 : return L"res\\paper\\paper_0.png"; break;
    case view::paper_type::paper_1 : return L"res\\paper\\paper_1.png"; break;
    case view::paper_type::paper_2 : return L"res\\paper\\paper_2.png"; break;
    case view::paper_type::paper_3 : return L"res\\paper\\paper_3.png"; break;
    case view::paper_type::paper_4 : return L"res\\paper\\paper_4.png"; break;
    }

    return nullptr;
}

inline view::paper_type commandIdToPaperType(const view::command_id id)
{
    switch(id)
    {
        case view::command_id::paper_0: return view::paper_type::paper_0;
        case view::command_id::paper_1: return view::paper_type::paper_1;
        case view::command_id::paper_2: return view::paper_type::paper_2;
        case view::command_id::paper_3: return view::paper_type::paper_3;
        case view::command_id::paper_4: return view::paper_type::paper_4;
    }

    return view::paper_type::none;
}

}} // namespace journal::common